package org.javahispano.javacup.tacticas_aceptadas.byterider;

import java.awt.Color;
import org.javahispano.javacup.modelo.*;
import java.util.List;
import java.util.LinkedList;
import java.util.ArrayList;

public class ByteriderFC implements Tactica
{
    Posicion alineacion5[]=new Posicion[]
    {
        new Posicion(0.2595419847328244,-50.41044776119403),
        new Posicion(-11.16030534351145,-35.78358208955224),
        new Posicion(12.717557251908397,-35.26119402985075),
        new Posicion(28.290076335877863,-28.470149253731343),
        new Posicion(-28.290076335877863,-28.470149253731343),
        new Posicion(14.793893129770993,-18.544776119402986),
        new Posicion(-17.389312977099234,-19.58955223880597),
        new Posicion(0.0,-15.0),
        new Posicion(5.969465648854961,-5.485074626865671),
        new Posicion(0.2595419847328244,-0.26119402985074625),
        new Posicion(15.0,-1.0)
    };

    Posicion alineacion6[]=new Posicion[]
    {
        new Posicion(0.2595419847328244,-50.41044776119403),
        new Posicion(-11.16030534351145,-35.78358208955224),
        new Posicion(12.717557251908397,-35.26119402985075),
        new Posicion(28.290076335877863,-28.470149253731343),
        new Posicion(-28.290076335877863,-28.470149253731343),
        new Posicion(14.793893129770993,-18.544776119402986),
        new Posicion(-17.389312977099234,-19.58955223880597),
        new Posicion(0.0,-15.0),
        new Posicion(6.4885496183206115,-6.529850746268657),
        new Posicion(-6.4885496183206115,-6.529850746268657),
        new Posicion(15.0,-1.0)
    };

    class TacticaDetalleImpl implements TacticaDetalle {

        public String getNombre() {
            return "ByteriderFC";
        }

        public String getPais() {
            return "España";
        }

        public String getEntrenador() {
            return "Byterider";
        }

        public Color getColorCamiseta() {
            return new Color(0, 153, 204);
        }

        public Color getColorPantalon() {
            return new Color(0, 0, 255);
        }

        public Color getColorFranja() {
            return new Color(0, 0, 0);
        }

        public Color getColorCalcetas() {
            return new Color(51, 51, 255);
        }

        public Color getColorPortero() {
            return new Color(153, 0, 0        );
        }

        public EstiloUniforme getEstilo() {
            return EstiloUniforme.SIN_ESTILO;
        }

        public Color getColorCamiseta2() {
            return new Color(255, 255, 255);
        }

        public Color getColorPantalon2() {
            return new Color(255, 0, 51);
        }

        public Color getColorFranja2() {
            return new Color(0, 0, 0);
        }

        public Color getColorCalcetas2() {
            return new Color(255, 255, 255);
        }

        public Color getColorPortero2() {
            return new Color(0, 102, 0        );
        }

        public EstiloUniforme getEstilo2() {
            return EstiloUniforme.SIN_ESTILO;
        }

        class JugadorImpl implements JugadorDetalle
        {
            String nombre;
            int numero;
            Color piel, pelo;
            double velocidad, remate, presicion;
            boolean portero;
            Posicion posicion;

            public JugadorImpl(String nombre, int numero, Color piel, Color pelo,
                    double velocidad, double remate, double presicion, boolean portero) {
                this.nombre=nombre;
                this.numero=numero;
                this.piel=piel;
                this.pelo=pelo;
                this.velocidad=velocidad;
                this.remate=remate;
                this.presicion=presicion;
                this.portero=portero;
            }

            public String getNombre() {
                return nombre;
            }

            public Color getColorPiel() {
                return piel;
            }

            public Color getColorPelo() {
                return pelo;
            }

            public int getNumero() {
                return numero;
            }

            public boolean esPortero() {
                return portero;
            }

            public double getVelocidad() {
                return velocidad;
            }

            public double getRemate() {
                return remate;
            }

            public double getPresicion() {
                return presicion;
            }

        }

        public JugadorDetalle[] getJugadores() {
            return new JugadorDetalle[]{
               new JugadorImpl("Caramon", 1, new Color(255,200,150), new Color(102,102,0),1.0d,1.0d,0.67d, true),
                new JugadorImpl("Suswen", 2, new Color(255,102,102), new Color(50,0,0),1.0d,1.0d,0.5d, false),
                new JugadorImpl("Naquio", 3, new Color(255,200,150), new Color(50,0,0),1.0d,1.0d,0.5d, false),
                new JugadorImpl("Fisioman", 4, new Color(255,200,150), new Color(153,153,0),1.0d,1.0d,0.56d, false),
                new JugadorImpl("Tango", 5, new Color(255,200,150), new Color(255,204,0),0.71d,0.7d,0.49d, false),
                new JugadorImpl("Dalamar", 6, new Color(255,200,150), new Color(50,0,0),0.67d,1.0d,0.69d, false),
                new JugadorImpl("Helios", 7, new Color(255,200,150), new Color(50,0,0),0.67d,1.0d,0.69d, false),
                new JugadorImpl("Lupine", 8, new Color(255,200,150), new Color(50,0,0),0.75d,1.0d,0.64d, false),
                new JugadorImpl("Ender", 9, new Color(255,200,150), new Color(102,51,0),0.87d,1.0d,0.79d, false),
                new JugadorImpl("Cauchemar", 10, new Color(255,200,150), new Color(255,0,51),0.85d,1.0d,0.75d, false),
                new JugadorImpl("SORP", 11, new Color(255,200,150), new Color(255,153,153),1.0d,1.0d,1.0d, false)
            };
        }
    }

    TacticaDetalle detalle=new TacticaDetalleImpl();
    public TacticaDetalle getDetalle() {
        return detalle;
    }

    public Posicion[] getPosicionSaca(SituacionPartido sp) {
    return alineacion5;
    }

    public Posicion[] getPosicionRecive(SituacionPartido sp) {
    return alineacion6;
    }


    // Array donde se indica si un rival ya esta defendido
    private boolean []jugadorContrarioDefendido = new boolean[11];

    // Posicion del balon
    private Posicion pBalonAnterior = null;
    private double alturaBalonAnterior;
    private Posicion balon;
    private double alturaBalon=0;
    private double distanciaRecorridaPorBalon;
    private boolean recienGolpeadoBalon = false;
    private int iteracionesRecienGolpeado = 0;

    // Recta que sigue el balon
    double mBalon,nBalon;

    // Detalle de jugadores
    private Posicion[] rivales;
    private JugadorDetalle[] detalleMisJugadores;
    private JugadorDetalle[] detalleRivales;
    private double [][] posicionFuturaBalon;
    private Posicion[] misJugadores;
    private double [][] distanciaEntreMisJugadoresYRivales;
    private double [] minDistanciaEntreMisJugadoresYRivales;
    private double [][] distanciaEntreMisJugadoresYMisJugadores;
    private double [] distanciaMisJugadoresBalon;
    private double [] distanciaRivalesBalon;
    private final static double MITAD_ANCHO = Constantes.ANCHO_CAMPO_JUEGO / 2;
    private final static double MITAD_LARGO = Constantes.LARGO_CAMPO_JUEGO / 2;

    private int porteroRival;
    private int jugadorTardaMenos=10;

    private boolean pendienteNoInfinita(Posicion a, Posicion b)
    {
        double denominador = a.getX()-b.getX();
        return (denominador != 0);
    }

    private double calculoPendiente (Posicion a, Posicion b)
    {
        double m = a.getY()-b.getY();
        if (m!=0)
        {
            double denominador = a.getX()-b.getX();
            if (denominador!=0)
            {
                m = m / denominador;
            }
        }
        if (m==Double.NaN)
            return 0;
        else
            return m;
    }

    private void InicializaDistancias()
    {
        distanciaEntreMisJugadoresYRivales = new double [11][11];
        minDistanciaEntreMisJugadoresYRivales = new double [11];
        double auxMin;
        for (int i=0;i<11;i++)
        {
            auxMin=200;
            for (int j=0;j<11;j++)
            {
                distanciaEntreMisJugadoresYRivales[i][j]=Math.sqrt(Math.pow (misJugadores[i].getX()-rivales[j].getX(),2) + Math.pow(misJugadores[i].getY()-rivales[j].getY(),2));
                if (distanciaEntreMisJugadoresYRivales[i][j]<auxMin)
                {
                    auxMin=distanciaEntreMisJugadoresYRivales[i][j];
                }
            }
            minDistanciaEntreMisJugadoresYRivales[i]=auxMin;
        }
        distanciaEntreMisJugadoresYMisJugadores = new double [11][11];
        for (int i=0;i<11;i++)
        {
            for (int j=0;j<11;j++)
            {
                distanciaEntreMisJugadoresYMisJugadores[i][j]=Math.sqrt(Math.pow (misJugadores[i].getX()-misJugadores[j].getX(),2) + Math.pow(misJugadores[i].getY()-misJugadores[j].getY(),2));
            }
        }
        distanciaMisJugadoresBalon = new double[11];
        for (int i=0;i<11;i++)
        {
           distanciaMisJugadoresBalon[i]=Math.sqrt(Math.pow (misJugadores[i].getX()-balon.getX(),2) + Math.pow(misJugadores[i].getY()-balon.getY(),2));
        }
        distanciaRivalesBalon = new double[11];
        for (int i=0;i<11;i++)
        {
           distanciaRivalesBalon[i]=Math.sqrt(Math.pow (rivales[i].getX()-balon.getX(),2) + Math.pow(rivales[i].getY()-balon.getY(),2));
        }
    }

    private int DecideAQuienPasar(int pasador,boolean adelante)
    {
        int aQuienPasar=10;
        ArrayList<Integer> quienes = new ArrayList<Integer>(11);

        quienes.clear();
        // Se realiza una criba inicial con los jugadores que estan cerca
        for (int j=1;j<11;j++)
        {
            if (((distanciaMisJugadoresBalon[j]<=40)&&(distanciaMisJugadoresBalon[j]>=10))&&(misJugadores[j].getY()>=misJugadores[pasador].getY())&&adelante)
            {
                quienes.add(j);
            }
            else if (((distanciaMisJugadoresBalon[j]<=40)&&(distanciaMisJugadoresBalon[j]>=10))&&(!adelante))
            {
                quienes.add(j);
            }
        }
        double maxSolo=0;
        for(int i=0;i<quienes.size();i++)
        {
            if (minDistanciaEntreMisJugadoresYRivales[quienes.get(i)]>maxSolo)
            {
                maxSolo=minDistanciaEntreMisJugadoresYRivales[quienes.get(i)];
                aQuienPasar = quienes.get(i);
            }
        }
        return aQuienPasar;
    }

    private double CalculaCalidadPase(Posicion donde,int aquien)
    {
        double calidadPase=0;
        double calidadPaseRivalAux=0;

        calidadPase = Math.sqrt( Math.pow (misJugadores[aquien].getX()-donde.getX(),2) + Math.pow(misJugadores[aquien].getY()-donde.getY(),2));
        for (int j=0;j<11;j++)
        {
            calidadPaseRivalAux = Math.sqrt( Math.pow (rivales[j].getX()-donde.getX(),2) + Math.pow(rivales[j].getY()-donde.getY(),2));
            if (calidadPaseRivalAux<calidadPase)
            {
                calidadPase=-calidadPaseRivalAux;
            }
        }
        return calidadPase;
    }

    private boolean dentroCampo(Posicion p)
    {
        return (p.getX() >= -MITAD_ANCHO && p.getX() <= MITAD_ANCHO &&
                p.getY() >= -MITAD_LARGO && p.getY() <= MITAD_LARGO);
    }

    private Posicion DondePasar(double precisionPasador,int jugador)
    {
        Posicion [] posiciones = new Posicion[17];
        double iteracionesDistancia = (int) (distanciaMisJugadoresBalon[jugador]/2.5);
        double delta = detalleMisJugadores[jugador].getVelocidad()*iteracionesDistancia*precisionPasador*precisionPasador;
        double deltaDiagonal = delta / Math.sqrt(2);

        // posicion relativa (0,0)
        double x=misJugadores[jugador].getX();
        double y=misJugadores[jugador].getY();
        posiciones[0] = new Posicion(x - deltaDiagonal, y - deltaDiagonal);
        posiciones[1] = new Posicion(x, y - delta);
        posiciones[2] = new Posicion(x + deltaDiagonal, y - deltaDiagonal);
        posiciones[3] = new Posicion(x + delta, y);
        posiciones[4] = new Posicion(x, y);
        posiciones[5] = new Posicion(x - delta, y);
        posiciones[6] = new Posicion(x - deltaDiagonal, y + deltaDiagonal);
        posiciones[7] = new Posicion(x, y + delta);
        posiciones[8] = new Posicion(x + deltaDiagonal, y + deltaDiagonal);
        posiciones[9] = new Posicion(x - (deltaDiagonal/2), y - (deltaDiagonal/2));
        posiciones[10] = new Posicion(x, y - (delta/2));
        posiciones[11] = new Posicion(x + (deltaDiagonal/2), y - (deltaDiagonal/2));
        posiciones[12] = new Posicion(x + (delta/2), y);
        posiciones[13] = new Posicion(x + (deltaDiagonal/2), y + (deltaDiagonal/2));
        posiciones[14] = new Posicion(x, y + (delta/2));
        posiciones[15] = new Posicion(x - (deltaDiagonal/2), y + (deltaDiagonal/2));
        posiciones[16] = new Posicion(x - (delta/2), y);

        double max = 0;
        double maxAux;
        int posicionMejor=4;

        for (int i=0;i<17;i++)
        {
            if (this.dentroCampo(posiciones[i]))
            {
                maxAux = this.CalculaCalidadPase(posiciones[i],jugador);
                if (maxAux>=max)
                {
                    max=maxAux;
                    posicionMejor=i;
                }
            }
        }
        return posiciones[posicionMejor];
    }

    private Posicion DondeHacerAutopase(double precisionPasador,int jugador,boolean adelante)
    {
        Posicion [] posiciones = new Posicion[16];
        double delta = Constantes.VELOCIDAD_MIN+detalleMisJugadores[jugador].getVelocidad()*(Constantes.VELOCIDAD_MAX-Constantes.VELOCIDAD_MIN);
        double deltaDiagonal = delta / Math.sqrt(2);
        double deltaSen = delta/2;
        double deltaCos = delta *Math.sqrt(3)/2;

        // posicion relativa (0,0)
        double x=misJugadores[jugador].getX();
        double y=misJugadores[jugador].getY();
        posiciones[0] = new Posicion(x + delta, y);
        posiciones[1] = new Posicion(x - delta, y);
        posiciones[2] = new Posicion(x - deltaDiagonal, y + deltaDiagonal);
        posiciones[3] = new Posicion(x, y + delta);
        posiciones[4] = new Posicion(x + deltaDiagonal, y + deltaDiagonal);
        posiciones[5] = new Posicion(x + deltaSen, y + deltaCos);
        posiciones[6] = new Posicion(x + deltaCos, y + deltaSen);
        posiciones[7] = new Posicion(x - deltaSen, y + deltaCos);
        posiciones[8] = new Posicion(x - deltaCos, y + deltaSen);
        posiciones[9] = new Posicion(x, y - delta);
        posiciones[10] = new Posicion(x + deltaDiagonal, y - deltaDiagonal);
        posiciones[11] = new Posicion(x - deltaDiagonal, y - deltaDiagonal);
        posiciones[12] = new Posicion(x - deltaSen, y - deltaCos);
        posiciones[13] = new Posicion(x - deltaCos, y - deltaSen);
        posiciones[14] = new Posicion(x + deltaSen, y - deltaCos);
        posiciones[15] = new Posicion(x + deltaCos, y - deltaSen);

        double max = 0;
        double maxAux;
        int posicionMejor=4;

        int hasta=16;
        if(adelante) hasta=9;
        for (int i=0;i<hasta;i++)
        {
            if (this.dentroCampo(posiciones[i]))
            {
                maxAux = this.CalculaCalidadPase(posiciones[i],jugador);
                if (maxAux>=max)
                {
                    max=maxAux;
                    posicionMejor=i;
                }
            }
        }
        return posiciones[posicionMejor];
    }

    private double CalculaMinDistanciaRivales(Posicion donde)
    {
        double d=0;
        double minD=1000;

        for (int j=0;j<11;j++)
        {
            d = Math.sqrt( Math.pow (rivales[j].getX()-donde.getX(),2) + Math.pow(rivales[j].getY()-donde.getY(),2));
            if (d<minD)
            {
                minD=d;
            }
        }
        return minD;
    }

    private Posicion DondeDespejar(Posicion desdeDonde)
    {
        double x = desdeDonde.getX();
        double y = desdeDonde.getY();
        double d =2.4;
        double dDiagonal = d / Math.sqrt(2);
        Posicion [] posiciones = new Posicion[3];
        posiciones[0] = new Posicion(x,y + d);
        posiciones[1] = new Posicion(x-dDiagonal,y + dDiagonal);
        posiciones[2] = new Posicion(x+dDiagonal,y + dDiagonal);

        double maxDistancia=0,maxAux=0;
        int mejorEleccion=2;
        for (int i=0;i<3;i++)
        {
            maxAux=this.CalculaMinDistanciaRivales(posiciones[i]);
            if (maxAux>maxDistancia)
            {
                maxDistancia=maxAux;
                mejorEleccion=i;
            }
        }
        return posiciones[mejorEleccion];
    }

    private double calculoInterseccionEjeY(Posicion a, Posicion b,double pendiente)
    {
        return (a.getY() - pendiente * a.getX());
    }

    private double[][] PredicePosicionBalon()
    {
        int iteraciones = 1000;
        double xbalon=0,ybalon=0,zbalon=0;
        double auxAlturaBalon = alturaBalon;
        boolean suelo=false;
        double[][] posicionFutura;
        posicionFutura = new double[iteraciones][3];
        posicionFutura[0][0] = balon.getX();
        posicionFutura[0][1] = balon.getY();
        posicionFutura[0][2] = auxAlturaBalon;

        zbalon=auxAlturaBalon-alturaBalonAnterior;
        for (int i=1;i<iteraciones;i++)
        {
            if (!suelo && auxAlturaBalon == 0 && zbalon >= 0 && zbalon < Constantes.G * 3)
            {//condicion para que se arrastre
                suelo = true;
            }
            if (suelo)
            {
                if (i==1)
                {
                    xbalon = (posicionFutura[i-1][0] - pBalonAnterior.getX()) * Constantes.FACTOR_DISMINUCION_VEL_BALON_SUELO;
                    ybalon = (posicionFutura[i-1][1] - pBalonAnterior.getY()) * Constantes.FACTOR_DISMINUCION_VEL_BALON_SUELO;
                }
                else
                {
                    xbalon = (posicionFutura[i-1][0] - posicionFutura[i-2][0]) * Constantes.FACTOR_DISMINUCION_VEL_BALON_SUELO;
                    ybalon = (posicionFutura[i-1][1] - posicionFutura[i-2][1]) * Constantes.FACTOR_DISMINUCION_VEL_BALON_SUELO;
                }
            }
            else
            {

                if (i==1)
                {
                    xbalon = (posicionFutura[i-1][0] - pBalonAnterior.getX()) * Constantes.FACTOR_DISMINUCION_VEL_BALON_AIRE;
                    ybalon = (posicionFutura[i-1][1] - pBalonAnterior.getY()) * Constantes.FACTOR_DISMINUCION_VEL_BALON_AIRE;
                }
                else
                {
                    xbalon = (posicionFutura[i-1][0] - posicionFutura[i-2][0]) * Constantes.FACTOR_DISMINUCION_VEL_BALON_AIRE;
                    ybalon = (posicionFutura[i-1][1] - posicionFutura[i-2][1]) * Constantes.FACTOR_DISMINUCION_VEL_BALON_AIRE;
                }

                auxAlturaBalon = Math.round(auxAlturaBalon + zbalon / Constantes.G) *Constantes.G;
                zbalon=Math.round((zbalon - Constantes.G) / Constantes.G) *Constantes.G;
                auxAlturaBalon = Math.round(posicionFutura[i-1][2] + zbalon / Constantes.G) *Constantes.G;
            }
            if (this.dentroCampo(new Posicion(posicionFutura[i-1][0],posicionFutura[i-1][1])))
            {
                posicionFutura[i][0] = posicionFutura[i-1][0] + xbalon;
                posicionFutura[i][1] = posicionFutura[i-1][1] + ybalon;
                posicionFutura[i][2] = posicionFutura[i-1][2] + zbalon;

                if (posicionFutura[i][2]<0)
                {
                    posicionFutura[i][2]=0;
                        zbalon = (-zbalon + Constantes.G) * Constantes.FACTOR_DISMINUCION_ALTURA_BALON_REBOTE;
                        zbalon = Math.round(zbalon / Constantes.G) *Constantes.G;
                }
            }
            else
            {
                posicionFutura[i][0] = posicionFutura[i-1][0];
                posicionFutura[i][1] = posicionFutura[i-1][1];
                posicionFutura[i][2] = 0;
            }
        }
        return posicionFutura;
    }


    private double PredicePosicionBalonFuturoTiro(double velocidad,Posicion donde,double anguloVertical)
    {
        int iteraciones = 1000;
        double zbalon=0;
        double auxAlturaBalon = 0.0;
        boolean suelo=false;
        double distanciaRecorrida=0.0;
        double distanciaTotal=Math.sqrt( Math.pow (balon.getX()-donde.getX(),2) + Math.pow(balon.getY()-donde.getY(),2));
        double alturaFutura=0.0,alturaFuturaAnterior=0.0;
        double angVer = anguloVertical;
        angVer = angVer * Math.PI / 180d;
        double vel = velocidad;
        if (vel != 0)
        {//si el remate tiene velocidad
            vel = vel * Math.cos(angVer);
            zbalon = Math.round(vel * Math.sin(angVer) / Constantes.G) * Constantes.G;
        }
        int i;
        for (i=1;(i<iteraciones)&&(distanciaRecorrida<distanciaTotal);i++)
        {
            if (!suelo && auxAlturaBalon == 0 && zbalon >= 0 && zbalon < Constantes.G * 3)
            {//condicion para que se arrastre
                suelo = true;
            }
            if (suelo)
            {
                distanciaRecorrida+=vel;
                vel = vel * Constantes.FACTOR_DISMINUCION_VEL_BALON_SUELO;
            }
            else
            {
                distanciaRecorrida+=vel;
                vel = vel * Constantes.FACTOR_DISMINUCION_VEL_BALON_AIRE;

                auxAlturaBalon = Math.round(auxAlturaBalon + zbalon / Constantes.G) *Constantes.G;
                if ((zbalon!=0.0)&&(i!=1))
                {
                    zbalon=Math.round((zbalon - Constantes.G) / Constantes.G) *Constantes.G;
                }
                if (auxAlturaBalon==0.0)
                {
                    zbalon = (-zbalon - Constantes.G) * Constantes.FACTOR_DISMINUCION_ALTURA_BALON_REBOTE;
                    zbalon = Math.round(zbalon / Constantes.G) *Constantes.G;
                }
            }
            alturaFutura = Math.round((alturaFuturaAnterior + zbalon)/Constantes.G)*Constantes.G;
            if ((alturaFuturaAnterior + zbalon)<0.01)
            {
                alturaFutura = 0.0;
            }
            alturaFuturaAnterior = alturaFutura;
        }
        return alturaFutura;
    }

    private double CalculaFuerzaTiro(int jugador,Posicion posicion)
    {
        double fuerzaRemate = 1.0;
        double factor=fuerzaRemate*detalleMisJugadores[jugador].getRemate();
        double velocidad = Constantes.REMATE_VELOCIDAD_MIN + (Constantes.DELTA_REMATE_VELOCIDAD)*factor;
        double alturaFuturaPosibleTiro = this.PredicePosicionBalonFuturoTiro(velocidad,posicion,Constantes.ANGULO_VERTICAL);

        while ((alturaFuturaPosibleTiro>Constantes.ALTO_ARCO)&&(fuerzaRemate>0.0))
        {
            fuerzaRemate-=0.01;
            factor=fuerzaRemate*detalleMisJugadores[jugador].getRemate();
            velocidad = Constantes.REMATE_VELOCIDAD_MIN + (Constantes.DELTA_REMATE_VELOCIDAD)*factor;
            alturaFuturaPosibleTiro = this.PredicePosicionBalonFuturoTiro(velocidad,posicion,Constantes.ANGULO_VERTICAL);
        }
        return fuerzaRemate;
    }

    private double CalculaFuerzaPase(int jugador,Posicion posicion)
    {
        double fuerzaPase = 1.0;
        double factor = fuerzaPase*detalleMisJugadores[jugador].getRemate();
        double velocidad = Constantes.REMATE_VELOCIDAD_MIN + (Constantes.DELTA_REMATE_VELOCIDAD)*factor;
        double alturaFuturaPosibleTiro = this.PredicePosicionBalonFuturoTiro(velocidad,posicion,Constantes.ANGULO_VERTICAL);

        while ((alturaFuturaPosibleTiro>Constantes.ALTURA_CONTROL_BALON)&&(fuerzaPase>0))
        {
            fuerzaPase-=0.01;
            factor=fuerzaPase*detalleMisJugadores[jugador].getRemate();
            velocidad = Constantes.REMATE_VELOCIDAD_MIN + (Constantes.DELTA_REMATE_VELOCIDAD)*factor;
            alturaFuturaPosibleTiro = this.PredicePosicionBalonFuturoTiro(velocidad,posicion,Constantes.ANGULO_VERTICAL);
        }
        return fuerzaPase;
    }

    private void MueveJugador(int jugador,Posicion pos)
    {
        comandos.add(new ComandoIrA(jugador, pos));
    }

    private void GolpeaBalon(int jugador,Posicion pos,double fuerza,boolean alto)
    {
        comandos.add(new ComandoGolpearBalon(jugador,pos,fuerza, alto));
        recienGolpeadoBalon = true;
    }

    private void GolpeaBalon(int jugador,Posicion pos,double fuerza,double angulo)
    {
        comandos.add(new ComandoGolpearBalon(jugador,pos,fuerza, angulo));
        recienGolpeadoBalon = true;
    }

    private int CalculaIteracionesParaLlegarABalon(Posicion pJugador,Posicion pBalon,double velocidad,boolean esPortero)
    {
        double distanciaABalon = Math.sqrt( Math.pow ((pBalon.getX()-pJugador.getX()),2) + Math.pow((pBalon.getY()-pJugador.getY()),2));
        if (esPortero&&(distanciaABalon>Constantes.DISTANCIA_CONTROL_BALON_PORTERO))
        {
            distanciaABalon -= Constantes.DISTANCIA_CONTROL_BALON_PORTERO;
            distanciaABalon = Math.abs(distanciaABalon);
        }
        else if (distanciaABalon>Constantes.DISTANCIA_CONTROL_BALON)
        {
            distanciaABalon -= Constantes.DISTANCIA_CONTROL_BALON;
            distanciaABalon = Math.abs(distanciaABalon);
        }
        double distanciaRecorridaPorTurno = Constantes.VELOCIDAD_MIN+velocidad*(Constantes.VELOCIDAD_MAX-Constantes.VELOCIDAD_MIN);

        // Iteraciones para llegar a la pelota por el jugador j en iteracion i
        double iteracionesAux = (distanciaABalon / distanciaRecorridaPorTurno);
        int iteraciones = (int) iteracionesAux;
        if (iteracionesAux>((double)iteraciones))
        {
            iteraciones++;
        }
        return iteraciones;
    }

    private int CalculaMinIteracionesParaLlegarABalon(Posicion[] pJugadores,double [][] pFuturaBalon,JugadorDetalle[] detalleJugadores,int jDesde,int jHasta)
    {
        boolean fin=false;
        int iteraciones,minIteracion=999;
        for(int i=0;(i<1000)&&!fin;i++)
        {
            for (int j=jDesde;j<=jHasta;j++)
            {
                iteraciones = this.CalculaIteracionesParaLlegarABalon(pJugadores[j],new Posicion(pFuturaBalon[i][0],pFuturaBalon[i][1]),detalleJugadores[j].getVelocidad(),detalleJugadores[j].esPortero());
                if ((iteraciones<minIteracion)&&(i>=iteraciones)&&(detalleJugadores[j].esPortero())&&(pFuturaBalon[i][2]<Constantes.ALTO_ARCO))
                {
                    minIteracion=iteraciones;
                    fin=true;
                    jugadorTardaMenos=j;
                }
                else if ((iteraciones<minIteracion)&&(i>=iteraciones)&&(!detalleJugadores[j].esPortero())&&(pFuturaBalon[i][2]<Constantes.ALTURA_CONTROL_BALON))
                {
                    minIteracion=iteraciones;
                    fin=true;
                    jugadorTardaMenos=j;
                }
            }
        }
        return minIteracion;
    }

    private void MuevePortero()
    {
        // Movimientos portero
        double mPortero,nPortero;
        double xPortero=Constantes.centroArcoInf.getX();
        double yPortero=Constantes.centroArcoInf.getY();

        // Se calcula la recta que sigue el balon
        double xAux;
        if (this.pendienteNoInfinita(pBalonAnterior, balon))
        {
            mBalon = this.calculoPendiente(pBalonAnterior, balon);
            nBalon = this.calculoInterseccionEjeY(pBalonAnterior, balon, mBalon);

            xAux = (Constantes.posteIzqArcoInf.getY()-nBalon)/mBalon;
        }
        else
        {
            mBalon = Double.POSITIVE_INFINITY;
            nBalon = pBalonAnterior.getX();
            xAux = balon.getX();
        }

        int minIteracion=999;
        int minIteracionRival=999;
        minIteracion=this.CalculaMinIteracionesParaLlegarABalon(misJugadores,posicionFuturaBalon,detalleMisJugadores,0,0);
        minIteracionRival = this.CalculaMinIteracionesParaLlegarABalon(rivales,posicionFuturaBalon,detalleRivales,0,10);

        // Si el balon va a porteria con fuerza
        if (((xAux>=Constantes.posteIzqArcoInf.getX())&&(xAux<=Constantes.posteDerArcoInf.getX())&&(mBalon!=Double.POSITIVE_INFINITY)&&distanciaRecorridaPorBalon>Constantes.DISTANCIA_CONTROL_BALON_PORTERO)
             ||(minIteracion<minIteracionRival))
        {
            if (minIteracion<minIteracionRival)
            {
                xPortero = posicionFuturaBalon[minIteracion][0];
                yPortero = posicionFuturaBalon[minIteracion][1];
            }
            else
            {
                yPortero = Constantes.centroArcoInf.getY()+Constantes.DISTANCIA_CONTROL_BALON_PORTERO;
                xPortero = (yPortero - nBalon) /mBalon;
            }
        }
        // Si no el portero se coloca en el punto medio entre la pelota y el centro de la porteria
        else if (this.pendienteNoInfinita(new Posicion(Constantes.centroArcoInf.getX(),Constantes.centroArcoInf.getY()-1), balon))
        {
            mPortero = this.calculoPendiente(balon,new Posicion(Constantes.centroArcoInf.getX(),Constantes.centroArcoInf.getY()-1));
            nPortero = this.calculoInterseccionEjeY(balon,new Posicion(Constantes.centroArcoInf.getX(),Constantes.centroArcoInf.getY()-1),mPortero);

            yPortero = Constantes.centroArcoInf.getY()+Constantes.DISTANCIA_CONTROL_BALON_PORTERO;
            xPortero =  (yPortero - nPortero) / mPortero;

            if (xPortero<Constantes.posteIzqArcoInf.getX()-1.5) xPortero=Constantes.posteIzqArcoInf.getX()-1.5;
            if (xPortero>Constantes.posteDerArcoInf.getX()+1.5) xPortero=Constantes.posteDerArcoInf.getX()+1.5;
        }
        else
        {
            yPortero = Constantes.centroArcoInf.getY()+Constantes.DISTANCIA_CONTROL_BALON_PORTERO;
            xPortero = balon.getX();
            if (xPortero<Constantes.posteIzqArcoInf.getX()-1.5) xPortero=Constantes.posteIzqArcoInf.getX()-1.5;
            if (xPortero>Constantes.posteDerArcoInf.getX()+1.5) xPortero=Constantes.posteDerArcoInf.getX()+1.5;
        }
        this.MueveJugador(0,new Posicion(xPortero,yPortero));
    }

    LinkedList<Comando> comandos = new LinkedList<Comando>();
    public List<Comando> ejecutar(SituacionPartido sp)
    {
        comandos.clear();
        if (recienGolpeadoBalon)
        {
            iteracionesRecienGolpeado--;
            if (iteracionesRecienGolpeado==0)
            {
                recienGolpeadoBalon=false;
            }
        }

        // Se obtiene una array con los jugadores que pueden rematar
        int rematan[] = sp.puedenRematar();

        // Inicializacion jugadores defendidos
        for (int i=0;i<11;i++)
        {
            jugadorContrarioDefendido[i]=false;
        }

        // Actualizamos la posicion del balon
        balon = new Posicion(sp.balon());
        alturaBalon = sp.alturaBalon();
        if(pBalonAnterior == null)
        {
            pBalonAnterior = sp.balon();
        }

        // Se predice la distancia recorrida por el balon
        distanciaRecorridaPorBalon = Math.sqrt( Math.pow (balon.getX()-pBalonAnterior.getX(),2) + Math.pow(balon.getY()-pBalonAnterior.getY(),2));

        // control de jugadores
        misJugadores = sp.misJugadores();
        rivales = sp.rivales();
        detalleMisJugadores = sp.detalleMisJugadores();
        detalleRivales = sp.detalleJugadoresRivales();

        double mPorteroRival,nPorteroRival;

        // Calculo de las posiciones futuras del balon
        posicionFuturaBalon = this.PredicePosicionBalon();

        this.InicializaDistancias();

        // Se descubre al portero rival
        for (int j=0;j<11;j++)
        {
            if (detalleRivales[j].esPortero())
            {
                porteroRival=j;
            }
        }

        /* MOVIMIENTO PORTERO */
        this.MuevePortero();

        /* MOVIMIENTO DEFENSAS */
        // Movimientos defensa al hombre
        for (int i=1; i<5; i++)
        {
            double x,y,m,n,a,b,c;

            // A quien defiendo?
            int aquiendefender=10;
            double distRival=300;
            for (int j=10;j>=0;j--)
            {
                if ((Math.sqrt( Math.pow (sp.rivales()[j].getX()-Constantes.centroArcoInf.getX(),2) + Math.pow(sp.rivales()[j].getY()-Constantes.centroArcoInf.getY(),2))<distRival)
                        &&(!jugadorContrarioDefendido[j]))
                {
                    distRival=Math.sqrt( Math.pow (sp.rivales()[j].getX()-Constantes.centroArcoInf.getX(),2) + Math.pow(sp.rivales()[j].getY()-Constantes.centroArcoInf.getY(),2));
                    aquiendefender=j;
                }
            }
            jugadorContrarioDefendido[aquiendefender]=true;

            if (this.pendienteNoInfinita(sp.rivales()[aquiendefender], sp.balon()))
            {
                m = this.calculoPendiente(sp.rivales()[aquiendefender], sp.balon());
                n = this.calculoInterseccionEjeY(sp.rivales()[aquiendefender], sp.balon(), m);

                a = m*m+1.0;
                b = 2.0*(m*(n-sp.rivales()[aquiendefender].getY())-sp.rivales()[aquiendefender].getX());
                c = sp.rivales()[aquiendefender].getX()*sp.rivales()[aquiendefender].getX() + (n-sp.rivales()[aquiendefender].getY())*(n-sp.rivales()[aquiendefender].getY()) -4;

                double bCuadrado = (b * b);
                double dosA = 2.0 * a;
                double auxSqrt = Math.sqrt(bCuadrado - (4.0 * a * c));
                double x1 = ((-b - auxSqrt) / (dosA));
                double x2 = ((-b + auxSqrt) / (dosA));

                if (sp.balon().getX()>sp.rivales()[aquiendefender].getX())
                {
                    x = x2;
                }
                else
                {
                    x = x1;
                }
                y = m * x + n;
            }
            else
            {
                x = sp.rivales()[aquiendefender].getX();
                if (sp.balon().getY()>sp.rivales()[aquiendefender].getY())
                {
                    y = sp.rivales()[aquiendefender].getY() + 2;
                }
                else
                {
                    y = sp.rivales()[aquiendefender].getY() - 2;
                }
            }
            this.MueveJugador(i, new Posicion(x,y));
        }

        /* MOVIMIETO DELANTEROS */
        if (balon.getY()<0)
        {
            if (balon.getX()>15)
            {
                this.MueveJugador(5, new Posicion(balon.getX()-45,balon.getY()+10));
                this.MueveJugador(6, new Posicion(balon.getX(),balon.getY()+10));
                this.MueveJugador(7, new Posicion(10.0,balon.getY()+20));
                this.MueveJugador(8, new Posicion(balon.getX(),balon.getY()+20));
                this.MueveJugador(9, new Posicion(balon.getX()-20,balon.getY()+30));
            }
            else if (balon.getX()<-15)
            {
                this.MueveJugador(5, new Posicion(balon.getX(),balon.getY()+10));
                this.MueveJugador(6, new Posicion(balon.getX()+45,balon.getY()+10));
                this.MueveJugador(7, new Posicion(-10.0,balon.getY()+20));
                this.MueveJugador(8, new Posicion(balon.getX()+20,balon.getY()+20));
                this.MueveJugador(9, new Posicion(balon.getX(),balon.getY()+30));
            }
            else
            {
                this.MueveJugador(5, new Posicion(balon.getX()-20,balon.getY()+10));
                this.MueveJugador(6, new Posicion(balon.getX()+20,balon.getY()+10));
                this.MueveJugador(7, new Posicion(balon.getX(),balon.getY()+20));
                this.MueveJugador(8, new Posicion(balon.getX()+20,balon.getY()+30));
                this.MueveJugador(9, new Posicion(balon.getX()-20,balon.getY()+30));
            }
            this.MueveJugador(10, new Posicion(balon.getX(),balon.getY()+50));
        }
        else
        {
            double yAux=balon.getY()+30;
            if (yAux>Constantes.LARGO_CAMPO_JUEGO/2.0)
                yAux=(Constantes.LARGO_CAMPO_JUEGO/2.0)-10;
            if (balon.getX()>15)
            {
                this.MueveJugador(5, new Posicion(balon.getX()-55,balon.getY()-5));
                this.MueveJugador(6, new Posicion(balon.getX(),balon.getY()-5));
                this.MueveJugador(7, new Posicion(10.0,balon.getY()-15));
                this.MueveJugador(8,new Posicion(balon.getX()-5,yAux));
                //this.MueveJugador(9,new Posicion(balon.getX()-25,yAux));
                this.MueveJugador(9,new Posicion(Constantes.posteIzqArcoSup.getX()+1,balon.getY()));
            }
            else if (balon.getX()<-15)
            {
                this.MueveJugador(5, new Posicion(balon.getX(),balon.getY()-5));
                this.MueveJugador(6, new Posicion(balon.getX()+55,balon.getY()-5));
                this.MueveJugador(7, new Posicion(-10.0,balon.getY()-15));
                //this.MueveJugador(8,new Posicion(balon.getX()+25,yAux));
                this.MueveJugador(8,new Posicion(Constantes.posteDerArcoSup.getX()-1,balon.getY()));
                this.MueveJugador(9,new Posicion(balon.getX()+5,yAux));
            }
            else
            {
                this.MueveJugador(5, new Posicion(balon.getX()-20,balon.getY()-5));
                this.MueveJugador(6, new Posicion(balon.getX()+20,balon.getY()-5));
                this.MueveJugador(7, new Posicion(balon.getX(),balon.getY()-15));
                this.MueveJugador(8,new Posicion(balon.getX()+10,yAux));
                this.MueveJugador(9,new Posicion(balon.getX()-10,yAux));
            }
            int portero=0;
            for (int i=0;i<11;i++)
            {
                if (detalleRivales[i].esPortero())
                {
                    portero=i;
                }
            }
            this.MueveJugador(10, new Posicion(rivales[portero].getX(),rivales[portero].getY()-4.9));
        }

        /* Movimiento del que tiene mas opciones de llegar a la pelota */
        // Se calcula el jugador que primero va a llegar al balon
        int minIteracion=this.CalculaMinIteracionesParaLlegarABalon(misJugadores,posicionFuturaBalon,detalleMisJugadores,1,10);

        double xMin = posicionFuturaBalon[minIteracion][0];
        double yMin = posicionFuturaBalon[minIteracion][1];
        this.MueveJugador(jugadorTardaMenos, new Posicion(xMin,yMin));

        // Remates
        mPorteroRival = this.calculoPendiente(sp.balon(),new Posicion(Constantes.centroArcoSup.getX(),Constantes.centroArcoSup.getY()));
        nPorteroRival = this.calculoInterseccionEjeY(sp.balon(),new Posicion(Constantes.centroArcoSup.getX(),Constantes.centroArcoSup.getY()),mPorteroRival);

        for (int i : rematan)
        {
            if ((balon.getY()>Constantes.centroArcoSup.getY()-4.8)
                    &&(balon.getX()>Constantes.posteIzqArcoSup.getX())
                    &&(balon.getX()<Constantes.posteDerArcoSup.getX()))
            {
                this.GolpeaBalon(i,new Posicion(sp.misJugadores()[i].getX(),Constantes.posteIzqArcoSup.getY()), 1.0, 20);
            }
            else if (Math.sqrt(Math.pow (sp.misJugadores()[i].getX()-Constantes.centroArcoSup.getX(),2) + Math.pow(sp.misJugadores()[i].getY()-Constantes.centroArcoSup.getY(),2))< 20)
            {
                if (rivales[porteroRival].getY()>mPorteroRival*rivales[porteroRival].getX()+nPorteroRival)
                {
                    if (mPorteroRival>0)
                    {
                        this.GolpeaBalon(i,new Posicion(Constantes.posteDerArcoSup.getX()-1,Constantes.posteDerArcoSup.getY()), 1.0, 20);
                    }
                    else
                    {
                        this.GolpeaBalon(i,new Posicion(Constantes.posteIzqArcoSup.getX()+1,Constantes.posteIzqArcoSup.getY()), 1.0, 20);
                    }
                }
                else
                {
                    if (mPorteroRival>0)
                    {
                        this.GolpeaBalon(i,new Posicion(Constantes.posteIzqArcoSup.getX()+1,Constantes.posteIzqArcoSup.getY()), 1.0, 20);
                    }
                    else
                    {
                        this.GolpeaBalon(i,new Posicion(Constantes.posteDerArcoSup.getX()-1,Constantes.posteDerArcoSup.getY()), 1.0, 20);
                    }
                }
            }
            else if (Math.sqrt(Math.pow (sp.misJugadores()[i].getX()-Constantes.centroArcoSup.getX(),2) + Math.pow(sp.misJugadores()[i].getY()-Constantes.centroArcoSup.getY(),2)) < 30)
            {
                double fuerza = this.CalculaFuerzaTiro(i,Constantes.centroArcoSup);

                if (rivales[porteroRival].getY()>mPorteroRival*rivales[porteroRival].getX()+nPorteroRival)
                {
                    if (mPorteroRival>0)
                    {
                        this.GolpeaBalon(i,new Posicion(Constantes.posteDerArcoSup.getX()-1,Constantes.posteDerArcoSup.getY()), fuerza , true);
                    }
                    else
                    {
                        this.GolpeaBalon(i,new Posicion(Constantes.posteIzqArcoSup.getX()+1,Constantes.posteIzqArcoSup.getY()), 1.0, 20);
                    }
                }
                else
                {
                    if (mPorteroRival>0)
                    {
                        this.GolpeaBalon(i,new Posicion(Constantes.posteIzqArcoSup.getX()+1,Constantes.posteIzqArcoSup.getY()), 1.0, 20);
                    }
                    else
                    {
                        this.GolpeaBalon(i,new Posicion(Constantes.posteDerArcoSup.getX()-1,Constantes.posteDerArcoSup.getY()), fuerza , true);
                    }
                }
            }
            else
            {
                if ((sp.balon().getX()==0)&&(sp.balon().getY()==0))
                {
                    this.GolpeaBalon(i,Constantes.cornerInfIzq, 1.0, true);
                }
                else
                {
                    int aQuienPasar;
                    if (balon.getY()>Constantes.centroArcoSup.getY()-15)
                    {
                        aQuienPasar = this.DecideAQuienPasar(i,false);
                    }
                    else
                    {
                        aQuienPasar = this.DecideAQuienPasar(i,true);
                    }
                    if (i==aQuienPasar)
                    {
                        // Autopase
                        Posicion dondeAutopase;
                        if (balon.getY()>Constantes.centroArcoSup.getY()-10)
                            dondeAutopase = this.DondeHacerAutopase(detalleMisJugadores[i].getPresicion(),i,false);
                        else                            
                            dondeAutopase = this.DondeHacerAutopase(detalleMisJugadores[i].getPresicion(),i,true);
                        comandos.add(new ComandoGolpearBalon(i));
                        this.MueveJugador(i, dondeAutopase);
                    }
                    else
                    {
                        if ((i==0)&&(minDistanciaEntreMisJugadoresYRivales[0]<7))
                        {
                            Posicion dondeDespejar = this.DondeDespejar(misJugadores[0]);
                            this.GolpeaBalon(i,dondeDespejar , 1.0, true);
                        }
                        else
                        {
                            Posicion dondePasar = this.DondePasar(detalleMisJugadores[i].getPresicion(),aQuienPasar);
                            double fuerzaPase = this.CalculaFuerzaPase(i,dondePasar);
                            this.GolpeaBalon(i,dondePasar , fuerzaPase, true);

                            iteracionesRecienGolpeado = Constantes.ITERACIONES_GOLPEAR_BALON;
                        }
                    }
                }
            }
        }

        // Se actualiza la posicion del balon para la siguiente iteraccion
        pBalonAnterior = sp.balon();
        alturaBalonAnterior = sp.alturaBalon();

        return comandos;
    }
}